const express = require('express')
const path = require('path')
const fs = require('fs')
const readline = require('readline')

// 定义一周前的时间（以毫秒为单位）
const oneWeekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000

// 定义一个月前的时间（以毫秒为单位）
const oneMonthAgo = new Date()
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1)

// 正则表达式匹配日志行
const log_Pattern = /requested 'GET \/([^']+)'/
const log_TimePattern = /\[(.*?)\] .* requested 'GET \/([^']+)'/

const log_PatternPm2 = /requested 'GET \/([^']+)'/
const log_TimePatternPm2 = /\[(.*?)\]http --- .* requested 'GET \/([^']+)'/

// 插件入口
class MyCustomPlugin {
  constructor(config, options) {
    this.config = config // config.yml文件下middlewares配置
    this.options = options // verdaccio实例的配置
  }

  // 注册中间件
  register_middlewares(app, auth, storage) {
    /**
     * logs_path verdaccio 操作日志路径
     * storage_path .verdaccio-db.json本地包数据存储文件(storage可以直接register_middlewares接参storage上直接获取)
     */
    const { logs_path, storage_path } = this.config

    // 调试-日志文件路径
    const logFile = 'E:\\CET2025\\verdaccio\\logs\\3th-verdaccio-out.log'

    // 私有化包列表
    const storagePlugin_list = storage?.localStorage?.storagePlugin?.data?.list

    // listen
    const listen_path = storage?.config?.listen

    //
    const { isPm2Hosting } = this.config

    /**
     * ### 添加自定义路由
     * @description: 自定义页面路由
     * /custom-page
     */
    app.get('/custom-download-logs', (req, res) => {
      const filePath = path.join(__dirname, 'static', 'custom-download-logs.html')
      fs.readFile(filePath, 'utf8', (err, data) => {
        if (err) {
          res.status(500).send('Error loading custom page')
        } else {
          res.send(data)
        }
      })
    })

    /**
     * ### 获取pm2 verdaccio日志
     * @description: verdaccio由pm2 启动, 所以需要读取pm2 verdaccio日志, 用于解析下载日志
     * 需考虑日志文件过大使用日志轮转分割存储
     */
    app.get('/api/getPm2VerdaccioLogs', async (req, res) => {
      const logs = await getPm2VerdaccioLogs()
      console.log('读取日志成功')

      const filterLogs = (logs) => {
        const packageTotal = []
        const packageDetail = {}

        const packageArray = []

        storagePlugin_list.map((item) => {
          packageArray.push({
            name: item,
            downloadNum: logs.downloadStats[item]
          })
        })

        storagePlugin_list.forEach((pkg) => {
          let totalDownloads = 0
          let totalWeekDownloads = 0
          let totalMonthDownloads = 0

          packageDetail[pkg] = {
            total: 0,
            details: []
          }

          for (const key in logs.downloadStats) {
            if (logs.downloadStats.hasOwnProperty(key)) {
              // Check if the key is exactly the package name or contains the package name and ends with .tgz
              if (key === pkg || (key.includes(pkg) && key.endsWith('.tgz'))) {
                totalDownloads += logs.downloadStats[key]
                packageDetail[pkg].details.push({
                  packageName: key,
                  version: key,
                  value: logs.downloadStats[key],
                  weekDownloads: ''
                })
              }
            }
          }
          for (const key in logs.oneWeekAgoPackageDownloads) {
            if (logs.oneWeekAgoPackageDownloads.hasOwnProperty(key)) {
              // Check if the key is exactly the package name or contains the package name and ends with .tgz
              if (key === pkg || (key.includes(pkg) && key.endsWith('.tgz'))) {
                totalWeekDownloads += logs.oneWeekAgoPackageDownloads[key]
                packageDetail[pkg].details.forEach((item) => {
                  if (item.packageName === key) {
                    item.weekDownloads = logs.oneWeekAgoPackageDownloads[key]
                  }
                })
              }
            }
          }
          for (const key in logs.oneMonthAgoPackageDownloads) {
            if (logs.oneMonthAgoPackageDownloads.hasOwnProperty(key)) {
              // Check if the key is exactly the package name or contains the package name and ends with .tgz
              if (key === pkg || (key.includes(pkg) && key.endsWith('.tgz'))) {
                totalMonthDownloads += logs.oneMonthAgoPackageDownloads[key]
                packageDetail[pkg].details.forEach((item) => {
                  if (item.packageName === key) {
                    item.monthDownloads = logs.oneMonthAgoPackageDownloads[key]
                  }
                })
              }
            }
          }
          packageDetail[pkg].total = totalDownloads
          // packageTotal[pkg] = totalDownloads
          packageTotal.push({
            packageName: pkg,
            version: '',
            value: totalDownloads,
            totalWeekDownloads: totalWeekDownloads,
            totalMonthDownloads: totalMonthDownloads
          })
        })
        packageTotal.forEach((item) => {
          item.details = packageDetail[item.packageName].details
        })

        res.send({
          code: 0,
          msg: '读取日志记录成功',
          data: {
            // logs: logs,
            downloadLogs: packageTotal,
            config: this.config,
            options: this.options,
            auth: auth,
            storage: storage
          }
        })
      }

      filterLogs(logs)
    })

    /**
     * ### 根据配置的日志文件路径读取日志, 用于解析下载日志
     * @description: 日志文件过大采取流式读取
     * @returns
     */
    const getPm2VerdaccioLogs = () => {
      return new Promise((resolve, reject) => {
        try {
          // 创建可读流
          const fileStream = fs.createReadStream(logs_path, { encoding: 'utf-8' })

          // 创建逐行读取接口
          const rl = readline.createInterface({
            input: fileStream,
            crlfDelay: Infinity // 支持 Windows 和 Linux/macOS 的换行符
          })

          // 用于存储包下载量的对象
          const downloadStats = {}
          const oneWeekAgoPackageDownloads = {}
          const oneMonthAgoPackageDownloads = {}

          rl.on('line', (line) => {
            const logPattern = isPm2Hosting ? log_PatternPm2 : log_Pattern
            const logTimePattern = isPm2Hosting ? log_TimePatternPm2 : log_TimePattern

            // 统计总数
            const match = line.match(logPattern)
            if (match) {
              const packageName = decodeURIComponent(match[1])

              downloadStats[packageName] = (downloadStats[packageName] || 0) + 1
            }

            // 按时间筛选
            const matchTime = line.match(logTimePattern)
            if (matchTime) {
              const logTimeStr = matchTime[1]
              const packageName = decodeURIComponent(matchTime[2])

              // 解析日志时间
              const logTime = new Date(logTimeStr).getTime()

              // 如果日志时间在一周内，则统计下载量
              if (logTime >= oneWeekAgo) {
                if (!oneWeekAgoPackageDownloads[packageName]) {
                  oneWeekAgoPackageDownloads[packageName] = 0
                }
                oneWeekAgoPackageDownloads[packageName]++
              }

              // 如果日志时间在一月内，则统计下载量
              if (logTime >= oneWeekAgo) {
                if (!oneMonthAgoPackageDownloads[packageName]) {
                  oneMonthAgoPackageDownloads[packageName] = 0
                }
                oneMonthAgoPackageDownloads[packageName]++
              }
            }
          })

          rl.on('close', () => {
            resolve({
              downloadStats: downloadStats,
              oneWeekAgoPackageDownloads: oneWeekAgoPackageDownloads,
              oneMonthAgoPackageDownloads: oneMonthAgoPackageDownloads
            })
          })
        } catch {
          reject('Error reading log file')
        }
      })
    }

    console.log(`Plugin started successfully `)
    console.log(`Custom plugin page registered at ${listen_path}/custom-download-logs`)
  }
}

module.exports = (config, options) => new MyCustomPlugin(config, options)
